import sys
sys.path += ["/Library/Python/2.6/site-packages/ply-3.4-py2.6.egg"]

#import os
import re
import ply.lex as lex
import ply.yacc as yacc

html_begin = '''<html>
<head>
    <link href="jay.css" rel="stylesheet" type="text/css">
</head>
<body>
    <span class = "Key"> void </span>
    <span class = "Key"> main </span>
    <span class = "Key"> ( </span>
    <span class = "Key"> ) </span> '''

html_end = "</body>\n</html>"

code_begin = " <div class = \"CodeBlock\"> "
code_end = " </div> "

operator_begin = " <span class = \"Op\">"
keyword_begin = " <span class = \"Key\">"
literal_begin = " <span class = \"Lit\">"
separator_begin = " <span class = \"Sep\">"
identifier_begin = " <span class = \"Id\">"

span_end = "</span> "
new_line = " <br/> "

class Parser:
    # =================================================
    # Base class for a lexer/parser that has the rules 
    # defined as methods
    # ==================================================

    tokens = ()
    precedence = ()

    def __init__(self, **kw):
        self.debug = kw.get('debug', 0)
        self.names = { }        
        
        try:
            modname = os.path.split(os.path.splitext(__file__)[0]) \
            [1] + "_" + self.__class__.__name__
        except:
            modname = "parser"+"_"+self.__class__.__name__
        self.debugfile = modname + ".dbg"
        self.tabmodule = modname + "_" + "parsetab"        

        # Build the lexer and parser
        self.lexer = lex.lex(module=self, debug=self.debug)
        yacc.yacc(module=self,
                  debug=self.debug,
                  debugfile=self.debugfile,
                  tabmodule=self.tabmodule)
	
    def lineChar(self, lexpos):
		lexpos = min(lexpos, len(self.lexer.lexdata) - 1)
		nline = 1
		nchar = 1
        
		for i in xrange(lexpos):
			if self.lexer.lexdata[i] == "\n":
				nline += 1
				nchar = 1
			else: nchar += 1
		return nline, nchar
	
    def line(self, nline):
		if nline <= 0: return ""
		if nline > self.lexer.lexdata.count("\n") + 1: return ""
		return self.lexer.lexdata.split("\n")[nline-1].replace("\t", " ")
	
    def error(self, nchar, desc):
        lc = self.lineChar(nchar)
        
        pref = "  "
        line = pref + pref + self.line(lc[0] - 2) + "\n"
        line += pref + pref + self.line(lc[0] - 1) + "\n"
        line += pref + "> " + self.line(lc[0]) + "\n"
        line += pref + pref + self.line(lc[0] + 1) + "\n"        

        if nchar > 0:
            print >> sys.stderr, "  Line %d, character %d" % lc
            print >> sys.stderr, line            

        print >> sys.stderr, desc
        exit(1)
		
	
    def parse(self, s):
		return yacc.parse(s)

class Node:
    def __init__(self):
        self.code = ""
        self.ids = []
        self.block = False

class JParser(Parser):    
    reserved = {
        'boolean'   : 'BOOLEAN',
        'else'      : 'ELSE',
        'if'        : 'IF',
        'int'       : 'INT',
        'main'      : 'MAIN',
        'void'      : 'VOID',
        'while'     : 'WHILE'        
    }

    tokens = ['IDENTIFIER', 'LITERAL', 'LPAREN', 'RPAREN']
    tokens += ['LBRACE', 'RBRACE', 'COMMA', 'SEMICOLON']
    tokens += ['EQUALS', 'ADD', 'SUB', 'MUL', 'DIV', 'LOWER']
    tokens += ['LEQ', 'BIGGER', 'BEQ', 'EQUAL', 'NEQ', 'AND']
    tokens += ['OR', 'NOT'] + list(reserved.values())    
    
    bool = r"true|false"
    
    t_LITERAL =  r"[0-9]+"

    # separators
    t_LPAREN = r"\("
    t_RPAREN = r"\)"
    t_LBRACE = r"\{"
    t_RBRACE = r"\}"
    t_COMMA  = r","
    t_SEMICOLON = r";"

    # operators
    t_EQUALS = r"="
    t_ADD    = r"\+"
    t_SUB    = r"-"
    t_MUL    = r"\*"
    t_DIV    = r"/"
    t_LOWER  = r"<"
    t_LEQ    = r"<="
    t_BIGGER = r">"
    t_BEQ    = r">="
    t_EQUAL  = r"=="
    t_NEQ    = r"!="
    t_AND    = r"&&"
    t_OR     = r"\|\|"
    t_NOT    = r"!"

    t_ignore  = ' \t\r\f'


    def t_IDENTIFIER(self, t):
        r"[a-zA-Z][a-zA-Z0-9]*"
        if re.match(self.bool, t.value.lower()) == None:
            t.type = self.reserved.get(t.value.lower() ,'IDENTIFIER')
        else:
            t.type = 'LITERAL'
        return t

    def t_COMMENT(self, t):
        r'//.*'
        pass

    def t_newline(self, t):
        r'\n+'
        t.lexer.lineno += len(t.value)

    def t_error(self, t):        
        print "Error: illegal character '%s' in line '%s'" \
        % (t.value[0],t.lexer.lineno)
        t.lexer.skip(1)
        
    # ==============================
	# PARSING RULES
	# ==============================

    precedence = (
        ('left','ADD','SUB'),
        ('left','MUL','DIV'),
        ('left', 'LOWER', 'LEQ', 'BIGGER', 'BEQ', 'EQUAL', 'NEQ')
        )
    
    def p_program(self, p):		
        " program : VOID MAIN LPAREN RPAREN LBRACE prog "        
        
        # Lista con los ids
        #print p[6].ids
        
        s = []
        for id in p[6].ids:
            #print id[0], id[1]
            if id[0] == "Use" and not id[1] in s:
                input = self.lexer.lexdata.replace("\t", " ").replace("\n", " ")				
                i = input.find(" %s " % id[1]) + 1
                if i < 0: i = input.find("%s " % id[1])
                if i < 0: i = input.find(" %s" % id[1]) + 1
                if i < 0: i = input.find("%s" % id[1])                
                self.error(i, "SyntaxError: name '%s' is not defined" % id[1])
            if id[0] == "Def": 
                s.append(id[1])                
                       
        p[0] = html_begin + new_line
        p[0] += separator_begin + "{" + span_end
        p[0] += code_begin        
        p[0] += p[6].code        
        p[0] += html_end
        
    def p_prog_end(self, p):
        """ prog : RBRACE 
                  | declarations RBRACE 
                  | statements RBRACE 
                  | declarations statements RBRACE """
        p[0] = Node()
        
        if len(p) > 2:
            p[0].code = p[1].code
            p[0].ids = p[1].ids
            if len(p) > 3:
                p[0].code += p[2].code
                p[0].ids += p[2].ids
        
        p[0].code += code_end
        p[0].code += separator_begin + "}" + span_end    
            
    def p_declarations(self, p):
        """ declarations : declaration 
                         | declaration declarations """
        p[0] = Node()        
        
        p[0].code = p[1].code
        p[0].ids = p[1].ids
        
        if len(p) > 2:            
            p[0].code += p[2].code
            p[0].ids += p[2].ids

    def p_statements(self, p):
        """ statements : statement 
                       | statement statements """
        p[0] = Node()              
        
        p[0].code = p[1].code
        p[0].ids = p[1].ids
        
        if len(p) > 2:            
            p[0].code += p[2].code
            p[0].ids += p[2].ids

    def p_declaration(self, p):
        " declaration : type identifiers SEMICOLON"
        p[0] = Node()        
        
        p[0].code = p[1].code + p[2].code
        p[0].code += separator_begin + ";" + span_end + new_line
        p[0].ids = p[2].ids

    def p_type(self, p):
        """ type : INT 
                 | BOOLEAN """
        p[0] = Node()
        p[0].code = keyword_begin
        
        if p[1] == "int":
            p[0].code += "int "
        else:
            p[0].code += "boolean "
            
        p[0].code += span_end
    
    def p_identifiers(self, p):
        """ identifiers : IDENTIFIER 
                        | IDENTIFIER COMMA identifiers """
        p[0] = Node()               
        
        p[0].code = identifier_begin + p[1] + span_end
        p[0].ids = [['Def', p[1]]]
        
        if len(p) > 2:            
            p[0].code += separator_begin + "," + span_end
            p[0].code += p[3].code
            p[0].ids += p[3].ids        

    def p_statement_end(self,p):
        " statement : SEMICOLON "
        p[0] = Node()
        p[0].code = separator_begin + ";" + span_end + new_line
    
    def p_statement(self,p):
        """ statement : block 
                      | assignment
                      | ifStatement
                      | whileStatement """
        p[0] = Node()
        p[0].code = p[1].code
        p[0].ids = p[1].ids
        p[0].block = p[1].block

    def p_block(self, p):
        """ block : LBRACE RBRACE 
                  | LBRACE statements RBRACE """
        p[0] = Node()
        p[0].block = True
        
        p[0].code = separator_begin + "{" + span_end        
        
        if len(p) > 3:
            p[0].code += code_begin        
            p[0].code += p[2].code
            p[0].code += code_end
            p[0].ids = p[2].ids
        
        p[0].code += separator_begin + "}" + span_end + new_line

    def p_assignment(self, p):
        " assignment : IDENTIFIER EQUALS expression SEMICOLON"
        p[0] = Node()
        
        p[0].code = identifier_begin + p[1] + span_end        
        p[0].code += operator_begin + "=" + span_end
        p[0].code += p[3].code        
        p[0].code += separator_begin + ";" + span_end + new_line
        p[0].ids = [['Use', p[1]]] + p[3].ids

    def p_ifStatement(self, p):
        """ ifStatement : IF LPAREN expression RPAREN statement
                | IF LPAREN expression RPAREN statement ELSE statement 
        """
        p[0] = Node()
        
        p[0].code = keyword_begin + "if" + span_end        
        p[0].code += separator_begin + "(" + span_end        
        p[0].code += p[3].code        
        p[0].code += separator_begin + ")" + span_end
        
        if (not p[5].block):
            p[0].code += code_begin + p[5].code + code_end            
        else:
            p[0].code += new_line + p[5].code
        
        p[0].ids = p[3].ids + p[5].ids
        
        if len(p) > 6:
            p[0].code += keyword_begin + "else" + span_end  
            
            if (not p[7].block):
                p[0].code += code_begin + p[7].code + code_end
            else:
                p[0].code += new_line + p[7].code
                
            p[0].ids += p[7].ids
            
    def p_whileStatement(self, p):
        " whileStatement : WHILE LPAREN expression RPAREN statement "
        p[0] = Node()
        
        p[0].code = keyword_begin + "while" + span_end        
        p[0].code += separator_begin + "(" + span_end        
        p[0].code += p[3].code        
        p[0].code += separator_begin + ")" + span_end        
        
        if (not p[5].block):
            p[0].code += code_begin + p[5].code + code_end
        else:
            p[0].code += new_line + p[5].code
        
        p[0].ids = p[3].ids + p[5].ids

    def p_expression(self, p):
        """ expression : conjunction 
                       | conjunction OR expression """
        p[0] = Node()
        
        p[0].code = p[1].code
        p[0].ids = p[1].ids
        
        if len(p) > 2:
            p[0].code += operator_begin + "||" + span_end
            p[0].code += p[3].code  
            p[0].ids += p[3].ids

    def p_conjunction(self, p):
        """ conjunction : relation 
                        | relation AND conjunction """
        p[0] = Node()
        
        p[0].code = p[1].code
        p[0].ids = p[1].ids
        
        if len(p) > 2:
            p[0].code += operator_begin + "&&" + span_end
            p[0].code += p[3].code  
            p[0].ids += p[3].ids

    def p_relation(self, p):
        """ relation : addition 
                     | addition LOWER relation
                     | addition LEQ relation
                     | addition BIGGER relation 
                     | addition BEQ relation 
                     | addition EQUAL relation 
                     | addition NEQ relation """
        p[0] = Node()
        
        p[0].code = p[1].code
        p[0].ids = p[1].ids
        
        if len(p) > 2:
            p[0].code += operator_begin + p[2] + span_end
            p[0].code += p[3].code
            p[0].ids += p[3].ids
        
    def p_addition(self, p):
        """ addition : term 
                     | term ADD addition
                     | term SUB addition """
        p[0] = Node()
        
        p[0].code = p[1].code
        p[0].ids = p[1].ids
        
        if len(p) > 2:
            p[0].code += operator_begin + p[2] + span_end
            p[0].code += p[3].code
            p[0].ids += p[3].ids
        
    def p_term(self, p):
        """ term : negation 
                 | negation MUL term
                 | negation DIV term """
        p[0] = Node()
        
        p[0].code = p[1].code
        p[0].ids = p[1].ids
        
        if len(p) > 2:
            p[0].code += operator_begin + p[2] + span_end
            p[0].code += p[3].code
            p[0].ids += p[3].ids
        
    def p_negation(self, p):
        """ negation : factor 
                     | NOT factor """
        p[0] = Node()
        
        if len(p) > 2:
            p[0].code = operator_begin + "!" + span_end
            p[0].code += p[2].code
            p[0].ids = p[2].ids
        else:
            p[0].code = p[1].code
            p[0].ids = p[1].ids

    def p_factor_identifier(self, p):
        " factor : IDENTIFIER "
        p[0] = Node()
        
        p[0].code = identifier_begin + p[1] + span_end
        p[0].ids = [['Use', p[1].lower()]]
    
    def p_factor_literal(self, p):
        " factor : LITERAL "
        p[0] = Node()        
        p[0].code = literal_begin + p[1] + span_end        
        
    def p_factor_expr(self, p):
        " factor : LPAREN expression RPAREN "        
        p[0] = Node()
        
        p[0].code = separator_begin + "(" + span_end
        p[0].code += p[2].code
        p[0].code = separator_begin + ")" + span_end
        p[0].ids = p[2].ids
            
    def p_error(self, p):
        if p == None:
            self.error(self.lexer.lexpos, \
            "SyntaxError: incomplete sentence")			
        else:
            self.error(self.lexer.lexpos - 1, \
            "SyntaxError: invalid syntax at '%s'" % p.value)

if __name__ == '__main__':
    jparser = JParser()
    s = sys.stdin.read()
    while(s):
        out = jparser.parse(s)
        s = sys.stdin.read()
        print out
    exit(0)
